package com.zone.mybatis.datasource.pooled;

import com.zone.mybatis.datasource.unpooled.UnpooledDataSource;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * @author: zongzi
 * @description: TODO
 * @date: 2023/12/25
 */
public class PooledDataSource implements DataSource {

    private int poolMaximumIdleConnections = 5;
    private int poolMaximumActiveConnections = 10;

    private int waitTime = 20000;

    private final PooledState state = new PooledState();

    private final UnpooledDataSource dataSource;

    public PooledDataSource() {
        this.dataSource = new UnpooledDataSource();
    }


    public void pushConnection(PooledConnection connection) throws SQLException {
        synchronized (state) {
            state.getActiveConnections().remove(connection);
            if (state.getIdleConnections().size() < poolMaximumIdleConnections) {
                // 空闲列表未达到最大空闲连接数
                if (!connection.getRealConnection().getAutoCommit()) {
                    connection.getRealConnection().rollback();
                }
                state.getIdleConnections().add(connection);
            } else {
                if (!connection.getRealConnection().getAutoCommit()) {
                    connection.getRealConnection().rollback();
                }
                connection.invalidate();
                connection.getRealConnection().close();
            }
        }
    }

    public PooledConnection popConnection(String username, String password) throws SQLException {
        PooledConnection conn = null;
        while (conn == null) {
            synchronized (state) {
                if (state.getIdleConnections().size() > 0) {
                    conn = state.getIdleConnections().remove(0);
                } else {
                    if (state.getActiveConnections().size() < this.poolMaximumActiveConnections) {
                        Connection connection = dataSource.getConnection(username, password);
                        conn = new PooledConnection(connection, this);
                    } else {
                        PooledConnection oldestConnection = state.getActiveConnections().get(0);
                        if (oldestConnection.getCheckoutTimeStamp() < this.waitTime) {
                            try {
                                state.wait(this.waitTime);
                            } catch (InterruptedException e) {
                                break;
                            }
                        } else {
                            // 检出时间超过waitTime直接移除
                            Connection connection = dataSource.getConnection(username, password);
                            conn = new PooledConnection(connection, this);
                            state.getActiveConnections().remove(oldestConnection);
                            oldestConnection.invalidate();
                        }
                    }
                }
                if (conn != null) {
                    if (conn.isValid()) {
                        if (!conn.getRealConnection().getAutoCommit()) {
                            conn.getRealConnection().rollback();
                        }
                        conn.setCheckoutTimeStamp(System.currentTimeMillis());
                        conn.setLastUsedTimeStamp(System.currentTimeMillis());
                        state.getActiveConnections().add(conn);
                    } else {
                        // 获取到坏的连接
                        conn = null;
                    }
                }
            }
        }
        if (conn == null) {
            throw new SQLException("get connection fail");
        }
        return conn;
    }

    public int getPoolMaximumIdleConnections() {
        return poolMaximumIdleConnections;
    }

    public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
        this.poolMaximumIdleConnections = poolMaximumIdleConnections;
    }

    public int getPoolMaximumActiveConnections() {
        return poolMaximumActiveConnections;
    }

    public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
        this.poolMaximumActiveConnections = poolMaximumActiveConnections;
    }

    public void setDriver(String driver) {
        this.dataSource.setDrive(driver);
    }

    public void setUrl(String url) {
        this.dataSource.setUrl(url);
    }

    public void setUsername(String username) {
        this.dataSource.setUsername(username);
    }

    public void setPassword(String password) {
        this.dataSource.setPassword(password);
    }

    public int getWaitTime() {
        return waitTime;
    }

    public void setWaitTime(int waitTime) {
        this.waitTime = waitTime;
    }

    @Override
    public Connection getConnection() throws SQLException {
        return this.popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return this.popConnection(username, password).getProxyConnection();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}
